#include <list>
#include <vector>
#include <algorithm>

using namespace std;


template  <typename T1,  class container> class tContainer_t {
	typedef typename container::iterator iter_t;

public:
	//Default Constructor
	tContainer_t(){}

	//Destructor
	~tContainer_t(){
		for(iter_t iT1 = data.begin();iT1!=data.end();iT1++)
			delete(*iT1);
		data.clear();
	}
	

	//Operator[] implementation
	const T1* operator[](const unsigned int index)  {
		if (!(index<=(unsigned int)this->getNumOfElements()-1 && index>=0))
			throw std::out_of_range("Index is out of bounds");
		//Legal index
		else 
		{
			if (typeid(container)==typeid(std::vector<T1*>)) 
			{
				vector<T1*> * c = (vector<T1*>*)&data;

				return (*c)[index] ;
			}
			else 
			{
		 
				unsigned int location = 0; 
				iter_t it = data.begin();
				while(it!=data.end()) 
				{
					if (index==location)
						return *it;
					else 
					{
						location++;
						it++;
					}
				}
			}

		}
		
	}
	
	

	//Return True iff there are no elemnts in the data structure
	const bool isEmpty() const {    
		return data.empty();
	}
	
	//Returns the number of elements in the data structure
	const int getNumOfElements() const {
		return data.size();	
	}
	
	//Inserts an element at the end of the container
	bool insertAtEnd(T1* elem){ 
		int prev = data.size();
		data.push_back(elem); 	
		int after = data.size();
		if (after == prev+1) return true;
		else return false;
	}

	//Return the first element in the container
	const T1* getFirstElement() const {	
		return data.front();	
	}
	//Return the last element in the container
	const T1* getLastElement() const {
		return data.back(); 	
	}
	//Finds an element (T1*) by the value
	const T1* findByValue(const T1& val)   {											   
		iter_t matching_iter = find_if(data.begin(),data.end(),std::bind2nd(isContentEqual(), val));  
		if (matching_iter!=data.end())
			return *matching_iter;
		else return 0;
	}                                                                          
	//Removes and deletes and element - finds it by it's value
	bool removeAndDeleteSpecificElement(const T1& val){
		iter_t matching_iter = find_if(data.begin(),data.end(),std::bind2nd(isContentEqual(), val));  
		if (matching_iter!=data.end()) { 
			delete(*matching_iter);
			data.erase(matching_iter);
			return true;
		}
		else
			return false;
	
	}


	//Removes all elements from the container
	void removeAllElements(){
		data.clear();	
	}

	//Remove the element and return a pointer to it
	const T1* removeAndReturnPointer(const T1& element){
	iter_t matching_iter = find_if(data.begin(),data.end(),std::bind2nd(isContentEqual(), element));  
		if (matching_iter!=data.end()) {
			T1* RetVal = *matching_iter;
			data.erase(matching_iter);
			return (RetVal);
		}
		else
			return 0;
	
	}
	//Removes and deletes all elements
	void removeAndDeleteAllElements(){
		for(iter_t iT1 = data.begin();iT1!=data.end();iT1++)
			delete(*iT1);
		data.clear();
	}



////////////////////////////////////Private Part//////////////////////////////////////////////
private:
	container data;

	//A struct that inhertis from binary_function to allpw comaprison between contents of the container
	struct isContentEqual : std::binary_function<T1*, T1, bool> 
	{
		bool operator()  (const T1* t1, const T1& val) const
		{
			return (*t1)==val;
		}
	};
	// A private function for using within the Copy Constructor and the = overloading
	const tContainer_t& assign(const tContainer_t& t) {
		for (iter_t i=t->data.begin();i!=t->data.end();i++) {
			this->insertAtEnd(*i); 
		}
		return *this;
	}

	//Copy Constructor - private - avoid copying
	tContainer_t(const tContainer_t& tc_ref){
		for (iter_t i=tc_ref->data.begin();i!=tc_ref->data.end();i++) {
			this->insertAtEnd(*i); 
		}
	}

	//Assignment Operator - private - avoid copying
	const tContainer_t& operator=(const tContainer_t& tc_ref){
		if (this!=tc_ref)
			this->assign(tc_ref);
		return *this;
	}
};
